
package com.baomidou.mybatisplus.generator.engine;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.config.ConstVal;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.google.common.base.CaseFormat;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.jetbrains.annotations.NotNull;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.*;

/**
 * Velocity 模板引擎实现文件输出
 *
 * @author hubin
 * @since 2018-01-10
 */
public class VelocityTemplateEngine extends AbstractTemplateEngine {
    private VelocityEngine velocityEngine;

    {
        try {
            Class.forName("org.apache.velocity.util.DuckType");
        } catch (ClassNotFoundException e) {
            // velocity1.x的生成格式错乱 https://github.com/baomidou/generator/issues/5
            LOGGER.warn("Velocity 1.x is outdated, please upgrade to 2.x or later.");
        }
    }

    @Override
    public @NotNull VelocityTemplateEngine init(@NotNull ConfigBuilder configBuilder) {
        if (null == velocityEngine) {
            Properties p = new Properties();
            p.setProperty(ConstVal.VM_LOAD_PATH_KEY, ConstVal.VM_LOAD_PATH_VALUE);
            p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, StringPool.EMPTY);
            p.setProperty(Velocity.ENCODING_DEFAULT, ConstVal.UTF8);
            p.setProperty(Velocity.INPUT_ENCODING, ConstVal.UTF8);
            p.setProperty("file.resource.loader.unicode", StringPool.TRUE);
            velocityEngine = new VelocityEngine(p);
        }
        return this;
    }


    @Override
    public void writer(@NotNull Map<String, Object> objectMap, @NotNull String templatePath, @NotNull File outputFile) throws Exception {
        Template template = velocityEngine.getTemplate(templatePath, ConstVal.UTF8);
        try (FileOutputStream fos = new FileOutputStream(outputFile);
             OutputStreamWriter ow = new OutputStreamWriter(fos, ConstVal.UTF8);
             BufferedWriter writer = new BufferedWriter(ow)) {
            template.merge(new VelocityContext(objectMap), writer);
        }
        LOGGER.debug("模板:" + templatePath + ";  文件:" + outputFile);
    }

    @Override
    public @NotNull Map<String, Object> getObjectMap(@NotNull ConfigBuilder config, @NotNull TableInfo tableInfo) {
        Map<String, Object> objectMap = super.getObjectMap(config, tableInfo);
        String entity = config.getPackageConfig().getPackageInfo("Entity");
        objectMap.put("customEntity", entity + StringPool.DOT + config.getPackageConfig().getShortPackage());
        objectMap.put("shortPackage", config.getPackageConfig().getShortPackage());
        objectMap.put("requestAddPackage", config.getPackageConfig().getRequestAddBean());
        objectMap.put("requestSearchPackage", config.getPackageConfig().getRequestSearchBean());
        objectMap.put("responseDetailPackage", config.getPackageConfig().getResponseDetailBean());
        String entityName = tableInfo.getEntityName();
        if(entityName.endsWith("Do")) {
            entityName = entityName.substring(0, entityName.length() - 2);
        }
        objectMap.put("requestAddClass", entityName + "AddReq");
        objectMap.put("requestSearchClass", entityName + "SearchReq");
        objectMap.put("responseDetailClass", entityName + "DetailRes");
        objectMap.put("entityLombokModel", true);
        objectMap.put("chainModel", false);
        objectMap.put("restControllerStyle", true);
        objectMap.put("serviceVar",
                CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, tableInfo.getServiceName()));
        objectMap.put("mapperVar",
                CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, tableInfo.getMapperName()));

        Map<String, String> i18ErrorMap = config.getPackageConfig().getI18ErrorMap();
        Map<String, String> requestMappingMap = config.getPackageConfig().getRequestMappingMap();

        Set<String> tablePrefix = config.getStrategyConfig().getTablePrefix();
        tablePrefix = Objects.isNull(tablePrefix) ? new HashSet<>() : tablePrefix;
        Set<String> tableSuffix = config.getStrategyConfig().getTableSuffix();
        tableSuffix = Objects.isNull(tableSuffix) ? new HashSet<>() : tableSuffix;
        String dealTableName = tableInfo.getName();
        Optional<String> suffixOptional = tableSuffix.stream()
                .filter(suffix -> org.apache.commons.lang.StringUtils.endsWithIgnoreCase(tableInfo.getName(), suffix))
                .findAny();
        if(suffixOptional.isPresent()) {
            dealTableName = dealTableName.substring(0, dealTableName.length() - suffixOptional.get().length());
        }
        Optional<String> prefixOptional = tablePrefix.stream()
                .filter(prefix -> org.apache.commons.lang.StringUtils.startsWithIgnoreCase(tableInfo.getName(), prefix))
                .findAny();
        if(prefixOptional.isPresent()) {
            dealTableName = dealTableName.substring(prefixOptional.get().length());
        }

        objectMap.put("requestMapping", Objects.isNull(requestMappingMap) ?
                        dealTableName.replaceAll("_", "/") :
                        requestMappingMap.getOrDefault(tableInfo.getName().toLowerCase(), dealTableName.replaceAll("_", "/")));

        objectMap.put("i18", Objects.isNull(i18ErrorMap) ?
                        dealTableName.replaceAll("_", "-") :
                        i18ErrorMap.getOrDefault(tableInfo.getName().toLowerCase(), dealTableName.replaceAll("_", "-")));
        return objectMap;
    }

    @Override
    public @NotNull AbstractTemplateEngine batchOutput() {
        try {
            ConfigBuilder config = this.getConfigBuilder();
            List<TableInfo> tableInfoList = config.getTableInfoList();
            tableInfoList.forEach(tableInfo -> {
                Map<String, Object> objectMap = this.getObjectMap(config, tableInfo);
                Optional.ofNullable(config.getInjectionConfig()).ifPresent(t -> {
                    // 添加自定义属性
                    t.beforeOutputFile(tableInfo, objectMap);
                    // 输出自定义文件
                    outputCustomFile(t.getCustomFiles(), tableInfo, objectMap);
                });
                // entity
                outputEntity(tableInfo, objectMap);
                // mapper and xml
                outputMapper(tableInfo, objectMap);
                // service
                outputService(tableInfo, objectMap);
                // controller
                outputController(tableInfo, objectMap);
                //  addReq
                outputCustom(tableInfo, objectMap, "/templates/requestAdd.java.vm", "AddReq");
                outputCustom(tableInfo, objectMap, "/templates/requestSearch.java.vm", "SearchReq");
                outputCustom(tableInfo, objectMap, "/templates/responseDetail.java.vm", "DetailRes");
            });
        } catch (Exception e) {
            throw new RuntimeException("无法创建文件，请检查配置信息！", e);
        }
        return this;
    }

    private void outputCustom(TableInfo tableInfo, Map<String, Object> objectMap,String templatePath, String suffix) {
        String entityName = tableInfo.getEntityName();
        String entityPath = getPathInfo(OutputFile.entity);
        if (StringUtils.isNotBlank(entityName) && StringUtils.isNotBlank(entityPath)) {
            if(entityName.endsWith("Do")) {
                entityName = entityName.substring(0, entityName.length() - 2);
            }
            String entityFile = String.format((entityPath + File.separator + "%s%s" + suffixJavaOrKt()), entityName, suffix);
            outputFile(new File(entityFile), objectMap, templatePath, getConfigBuilder().getStrategyConfig().entity().isFileOverride());
        }
    }

    @Override
    public @NotNull String templateFilePath(@NotNull String filePath) {
        final String dotVm = ".vm";
        return filePath.endsWith(dotVm) ? filePath : filePath + dotVm;
    }
}